• 検索結果がありません。

MSG_PEEK|MSG_DONTWAIT);

ドキュメント内 ネットワークプログラミング (ページ 31-81)

データはbufにコピーされる

• ioctl(sockfd, FIONREAD , &nbytes);

使えるOSは限られる(Linuxでは使える)

write()

#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);

unsigned char buf[4];

ssize_t n;

buf[0] = 0x5a;

buf[1] = 0x5b;

buf[2] = 0x5c;

buf[3] = 0x5b;

if (write(sockfd, buf, 4) == -1) { perror("write error");

exit(1);

}

ソケットセンドバッファに余裕がないときにはブ ロックする(エラーにはならない)。

ブロックしないようにするにはノンブロックキグ ソケットオプションを使う(ノンブロッキングにす るとエラー処理とかでだいぶ行数が増える)。

socket send/receive buffer の大きさ

application

TCP

IP

datalink

application buffer write()

socket send buffer

user process kernel

socket receive buffer

application buffer

read()

socket send/receive buffer の大きさの調整

受信に関しては

Linux

では自動調節機能がある

多重読み出しを行うときにはあらかじめ大きくしてお く必要がある

echo 0 > /proc/sys/net/ipv4/tcp_timestamps

echo 1 > /proc/sys/net/ipv4/tcp_moderate_rcvbuf echo 4194304 > /proc/sys/net/core/wmem_max

echo 4194304 > /proc/sys/net/core/rmem_max

echo 4194304 > /proc/sys/net/core/wmem_default echo 4194304 > /proc/sys/net/core/rmem_default

echo 4096 131072 4194304 > /proc/sys/net/ipv4/tcp_rmem echo 4096 131072 4194304 > /proc/sys/net/ipv4/tcp_wmem

テスト環境、テスト方法 (2)

• PC(HP xw8600) 1

– Quad-Core Xeon (2.5GHz) x 2

(のうち

4core

を使用)

– 2GB memory – BroadCom GbE

– Hitachi SATA Disk (7200rpm, 32MB

バッファ

, 1TB) – OS: RedHat Enterprise Linux 5.2

ネットワークスイッチ

– Cisco Catalyst 2960G-24TC-L x 2, – Cisco Catalyst 2960G-8TC-L

up to 30

Daq Operator

ネットワークからのデータ読み取り

MB/s

8 24 40

各種パラメータはOS デフォルト

• Loggerはディスクに データをセーブしない 状態で稼動

• Linux

のソケットレシーブバッファの大きさはデ フォルトでは自動調節が有効になっている。

• socket()

直後の初期値は

/proc/sys/net/ipv4/tcp_rmem

にある2番目の 数値で約

86kB

ソケットレシーブバッファの初期値を大きくす ることにより全データ読めるようになった。

ネットワークパラメータの調整

% cat /proc/sys/net/ipv4/tcp_rmem

4096 87380 4194304

ネットワークパラメータ調整後

データのセーブなしの場合。

読み取りが順調に行えている

40

8 24

MB/s

ここまでのまとめ

ソケットファイルディスクリプタを取得するとあ とは通常のファイルの読み書きと同様

ファイルを読むときとは違って指定したサイズ が必ずしも読めるとは限らない。指定したサ イズ必ず読みたければそのような関数を作る 必要がある。

ネットワークバイトオーダー (1)

• unsigned char buf[10];

アドレスは

buf[0], buf[1], buf[2]

の順に大きくなる

• unsigned char buf[10];

write(sockfd, buf, 10)

とすると

buf[0], buf[1], buf[2] …

の順に送られる。

• read(sockfd, buf, 10);

着た順に

buf[0], buf[1], buf[2]

に格納される。

ネットワークバイトオーダー (2)

// intがどういう順番でメモリーに

// 入っているか調べるプログラム

#include <stdio.h>

int main(int argc, char *argv[]) {

int i;

union num_tag {

unsigned char c[sizeof(int)];

unsigned int num;

} u_num;

u_num.num = 0x01020304;

for (i = 0; i < sizeof(int); i++) {

printf("u_num.c[%d]: %p 0x%02x ¥n", i, &u_num.c[i], u_num.c[i]);

}

return 0;

}

出力 (i386)

u_num.c[0]: 0xbfbfe850 0x04 u_num.c[1]: 0xbfbfe851 0x03 u_num.c[2]: 0xbfbfe852 0x02 u_num.c[3]: 0xbfbfe853 0x01

ネットワークバイトオーダー (3)

big endian

では 0x 01020304 = 16909060

little endian

では 0x 04030201 = 67305985 ネットワークバイトオーダーは

big endian

0x 01 02 03 04 の順に送られてきたデータをread(sockfd, buf, 4)で読んだ場合

0x01 0x02 0x03 0x04 0x01 0x02 0x03 0x04

big endian little endian

buf buf

ネットワークバイトオーダー (4)

ホストオーダー⇔ネットワークバイトオーダー 変換関数

– htonl (host to network long)

– htons (host to network short)

– ntohl (network to host long)

– ntohs (network to host short)

daytime client (1)

• xinetd

内蔵サーバー

daytime (port 13)

• /etc/xinetd.d/daytime-stream

にて

disable = no

に変更して

service xinetd restart

• telnet localhost 13

すると現在日時が表示される

#include <sys/socket.h>

#include <sys/types.h>

#include <arpa/inet.h>

#include <netinet/in.h>

#include <errno.h>

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#define MAXLINE 1024

int main(int argc, char *argv[]) {

unsigned char line[MAXLINE + 1];

struct sockaddr_in servaddr;

char *ip_address = "127.0.0.1";

int port = 13;

int sockfd, n;

servaddr.sin_family = AF_INET;

servaddr.sin_port = htons(port);

n = inet_pton(AF_INET, ip_address, &servaddr.sin_addr);

if (n < 0) {

perror("inet_pton");

exit(1);

}

else if (n == 0) {

fprintf(stderr, "invalid address %s", ip_address);

exit(1);

}

if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket");

exit(1);

}

if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) { perror("connect");

exit(1);

}

for ( ; ; ) {

n = read(sockfd, line, MAXLINE);

if (n < 0) {

perror("read");

exit(1);

}

else if (n == 0) { printf("EOF¥n");

break;

}

line[n] = '¥0'; /* string termination */

printf("%s¥n", line);

}

if (close(sockfd) < 0) { perror("close");

exit(1);

}

return 0;

}

0123456789012345678901234 5 6 07 AUG 2012 13:02:10 JST¥r¥n¥0

[daq@localhost daytimeclient]$ ./daytimeclient | hexdump -vC

00000000 30 37 20 41 55 47 20 32 30 31 32 20 31 33 3a 30 |07 AUG 2012 13:0|

00000010 32 3a 31 30 20 4a 53 54 0d 0a 0a 45 4f 46 0a |2:10 JST...EOF.|

情報のありか

• Manual Page

Manual Pages

セクション

– 1 (Utility Program) – 2 (System call)

– 3 (Library) – 4 (Device)

– 5 (File format) – 6 (Game)

– 7 (Misc.)

– 8 (Administration)

Linux

だとこの他

– 3P (Posix)

Manual Pages

• man

コマンド

• Linux

のマニュアルページは

– http://www.kernel.org/doc/man-pages/

最新のマニュアルはここで読める。

利用している

kernel

library

等のバージョンに注 意する必要がある。

Manual Pages

• Header

READ(3P) POSIX Programmer's Manual READ(3P) READ(2) Linux Programmer's Manual READ(2)

• SYNOPSIS

• DESCRIPTION

• RETURN VALUE

• SEE ALSO

• EXAMPLE

Manual Pages(

例題

)

READ(2) Linux Programmer's Manual READ(2) NAME

read - read from a file descriptor SYNOPSIS

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);

DESCRIPTION

read() attempts to read up to count bytes from file descriptor fd into the buffer starting at buf.

RETURN VALUE

ERRORS

CONFORMING TO

SVr4, 4.3BSD, POSIX.1-2001.

NOTES

Manual Pages(

例題

)

READ(2) Linux Programmer's Manual READ(2) NAME

read - read from a file descriptor SYNOPSIS

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);

DESCRIPTION

read() attempts to read up to count bytes from file descriptor fd into the buffer starting at buf.

RETURN VALUE

ERRORS

CONFORMING TO

SVr4, 4.3BSD, POSIX.1-2001.

NOTES

Utility

• gettimeofday()

• nc

• tcpdump

wireshark (ex. ethereal)

gettimeofday() で現在時刻の取得

struct timeval start, end, diff;

if (gettimeofday(&start, NULL) < 0) { err(1, "gettimeofday");

}

/* ... */

if (getimeofday(&end, NULL) < 0) { err(1, "gettimeofday");

}

/* 時間差をとるには引き算してもよいし、timersub()関数を使ってもよい timersub(&end, &start, &diff);

printf("%ld.%06ld¥n", result.tv_sec, result.tv_usec);

#include <sys/time.h>

int gettimeofday(struct timeval *tv, struct timezone *tz);

Linuxではgettimeofday()1,000,000回繰り返して1秒以下(CPUに依存する)

struct timeval {

time_t tv_sec; /* seconds */

suseconds_t tv_usec; /* microseconds */

};

ナノ秒まで必要なとき

clock_gettime(CLOCK_REALTIME, &ts);

コンパイル時に-lrtが必要 struct timespec {

time_t tv_sec; /* seconds */

long tv_nsec; /* nanoseconds */

};

余談: 最近のファイルシステムのタイムスタンプはナノ秒まで記録されている

% touch X

% ls -l --full X

-rw-rw-r-- 1 sendai sendai 0 2012-08-02 15:02:55.362116699 +0900 X

nc (netcat)

• nc - arbitrary TCP and UDP connections and listens

• nc 192.168.0.16 > datafile

で接続してデータを とってみる

• nc 192.168.0.16 | tee log.dat | prog_histo

% nc -l 1234

(これで待機して別の端末から) Hello, world

% nc 127.0.0.1 1234 Hello, world

tcpdump

ネットワーク上を流れているパケットを見るコマンド

接続できないんだけどパケットはでているのか?

データが読めないんだけど向こうからパケットは きているんでしょうか?

• root

にならないと使えない

起動方法

# tcpdump -n -w dumpfile -i eth0 # tcpdump -n -r dumpfile

• Selector

# tcpdump -n -r host 192.168.0.16

# tcpdump -n -r src 192.168.0.16 and dst 192.168.0.17

tcpdump 出力例

TCP

3way

ハンドシェイク付近:

11:27:55.137827 IP 192.168.0.16.59448 > 192.168.0.17.http: S 153443204:

153443204(0) win 5840 <mss 1460,sackOK,timestamp 587094474 0,nop,wscale 7>

11:27:55.139573 IP 192.168.0.17.http > 192.168.0.16.59448: S 4091282933:

4091282933(0) ack 153443205 win 65535 <mss 1460,nop,wscale 1,nop,nop,timestamp 3029380287 587094474,sackOK,eol>

11:27:55.139591 IP 192.168.0.16.59448 > 192.168.0.17.http: . ack 1 win 46

<nop,nop,timestamp 587094479 3029380287>

11:27:55.139751 IP 192.168.0.16.59448 > 192.168.0.17.http: P 1:103(102) ack 1 win 46 <nop,nop,timestamp 587094479 3029380287>

11:27:55.143520 IP 192.168.0.17.http > 192.168.0.16.59448: P 1:252(251) ack103 win 33304 <nop,nop,timestamp 3029380290 587094479>

tcpdump - 時刻情報

絶対時刻ではなくて相対的な時間に変換する プログラムを作っておくと便利なことがある。

0.000000 0.000000 IP 192.168.0.16.59448 > 192.168.0.17.http: S 153443204:1534432 0.001746 0.001746 IP 192.168.0.17.http > 192.168.0.16.59448: S 4091282933:409128 0.001764 0.000018 IP 192.168.0.16.59448 > 192.168.0.17.http: . ack 1 win 46 <nop 0.001924 0.000160 IP 192.168.0.16.59448 > 192.168.0.17.http: P 1:103(102) ack 1 0.005693 0.003769 IP 192.168.0.17.http > 192.168.0.16.59448: P 1:252(251) ack 10 0.005703 0.000010 IP 192.168.0.16.59448 > 192.168.0.17.http: . ack 252 win 54 <n 1.107822 1.102119 IP 192.168.0.16.59448 > 192.168.0.17.http: F 103:103(0) ack 25 1.108482 0.000660 IP 192.168.0.17.http > 192.168.0.16.59448: . ack 104 win 33304 1.109608 0.001126 IP 192.168.0.17.http > 192.168.0.16.59448: F 252:252(0) ack 10 1.109618 0.000010 IP 192.168.0.16.59448 > 192.168.0.17.http: . ack 253 win 54 <n

最初の欄はSYNを送ってからの経過時間

2番目の欄は直前の行との時間差を示すもの

tcpdump + program log

• tcpdump

の時刻情報と同じ時刻フォーマットでロ

グを出すようにしておいて

tcpdump

をとりつつプ ログラムを走らせあとからマージする:

(tcpdump -n -r tcpdump.out; cat log) | sort -n

NEUNET Protocol

クライアント サーバー(検出器モジュール)

length request

length + data

length request

length + data

tcpdump + program log

0.000000 0.000000 connect start

0.000063 0.000063 IP 192.168.0.204.57447 > 192.168.0.20.telnet: S 4076228960:407 0.000128 0.000065 IP 192.168.0.20.telnet > 192.168.0.204.57447: S 3718362368:371 0.000159 0.000031 IP 192.168.0.204.57447 > 192.168.0.20.telnet: . ack 1 win 5840 0.000215 0.000056 write length

0.000227 0.000012 IP 192.168.0.204.57447 > 192.168.0.20.telnet: P 1:9(8) ack 1 w 0.000234 0.000007 read length + data

0.000275 0.000041 IP 192.168.0.20.telnet > 192.168.0.204.57447: . ack 9 win 6551 0.002269 0.001994 IP 192.168.0.20.telnet > 192.168.0.204.57447: . 1:5(4) ack 9 w 0.002284 0.000015 IP 192.168.0.204.57447 > 192.168.0.20.telnet: . ack 5 win 5840 0.002300 0.000016 write length

0.002306 0.000006 IP 192.168.0.204.57447 > 192.168.0.20.telnet: P 9:17(8) ack 5 0.002312 0.000006 read length + data

0.002369 0.000057 IP 192.168.0.20.telnet > 192.168.0.204.57447: . ack 17 win 655 0.002568 0.000199 IP 192.168.0.20.telnet > 192.168.0.204.57447: . 5:1465(1460) a 0.002583 0.000015 IP 192.168.0.204.57447 > 192.168.0.20.telnet: . ack 1465 win 8 0.002717 0.000134 IP 192.168.0.20.telnet > 192.168.0.204.57447: . 1465:2925(1460

wireshark

• yum install wireshark-gnome (GUI

つきのをイ ンストールする)

• Ethernet

IP, TCP

のヘッダがどこか色つきで 表示してくれるので便利

あまり使ったことがないので教えてください

MACアドレスか らベンダーを調 べて表示する

(らしい)

wireshark

複数の

TCP

セッションが あっても

Analyze→Follow TCP

Stream

で追跡可能

wireshark

wireshark

データのダンプもできる

wireshark

フローグラフ

tcpflow

• tcpdump

wireshark

等でキャプチャしたファイ ルからデータフローを取り出すことができる

同様なソフトウェアは他にもある

ビットシフト、マスク

ヘッダ情報、データのデコードの際に必要になること がある。

ビットを節約するため等の理由により、1バイト内に 意味が違うデータが入っている場合にビットシフト、

マスク等を使用してデータを取り出すことが必要で ある場合がある。

取り出したあとは構造体メンバーに代入する

クレートナンバー モジュール ナンバー

多重読み出し

read(sockfd_0, buf_0, sizeof(buf_0));

read(sockfd_1, buf_1, sizeof(buf_1));

read(sockfd_2, buf_2, sizeof(buf_2));

とするとsockfd_0で止まると、sockfd_1が読めるようになっていても プログラムが進行しない。

読めるようになったものをどんどん読むにはselect()あるいはLinuxなら epoll()を使う。

あるいはpthreadを使う。

tcpdump で問題切り分けの例

• MLF

中性子

BL 01

での例

読み出しモジュールは

NEUNET

モジュール

NEUNET Protocol

PC

DAQ) NEUNET

モジュール

length request

length + data

length request

length + data

length request

length + data

length data data

読み取り側がまず、読み取りたいデータ長を 指定する。

NEUNETモジュール側では、まず、送ってく

るデータ長を送ってきて続いてデータを送っ てくる。

(問題点)

読み出しが

Too Much Data

というエラーを出し て止まることがある

問題切り分けのために正常にデータがきてい

るか

tcpdump

でダンプをとっていただいた

正常時のパケットの流れ

DAQPC NEUNETモジュール

TCP Connection Establised

length request

length reply + data reply length request

length reply + TCP SYN

TCP ACK + SYN TCP ACK

BL01 Too Many Data

時のパケット交換図

DAQPC NEUNETモジュール

Length request

TCP接続後いきなりデータがきていた (データは 0x47 47 47 47 …)

TCP connection established

( 1) 0.000000 0.000000 IP 192.168.0.2.57446 > 192.168.0.17.telnet: S 982133679:9 82133679(0) win 5840 <mss 1460,nop,nop,sackOK,nop,wscale 8>

( 2) 0.000054 0.000054 IP 192.168.0.17.telnet > 192.168.0.2.57446: S 2302400311:

2302400311(0) ack 982133680 win 1024 <mss 1460>

( 3) 0.000063 0.000009 IP 192.168.0.2.57446 > 192.168.0.17.telnet: . ack 1 win 5 840

( 4) 0.000290 0.000227 IP 192.168.0.17.telnet > 192.168.0.2.57446: . 1:1461(1460 ) ack 1 win 65519

0x0000: 4500 05dc a6fc 4000 8006 ccbb c0a8 0011 E...@...

0x0010: c0a8 0002 0017 e066 893b d738 3a8a 2bb0 ...f.;.8:.+.

0x0020: 5010 ffef 405f 0000 4747 4747 4747 4747 P...@_..GGGGGGGG 0x0030: 4747 4747 4747 4747 4747 4747 4747 4747 GGGGGGGGGGGGGGGG 0x0040: 4747 4747 4747 4747 4747 4747 4747 4747 GGGGGGGGGGGGGGGG 0x0050: 4747 4747 4747 4747 4747 4747 4747 4747 GGGGGGGGGGGGGGGG

BL01

でのダンプの解析:

RUN_29 (

その

1)

(4)のパケットデータ1460バイト全部0x47 TCP接続完了

TCP+IP Header

BL01

でのダンプの解析:

Run_29 (

その

2)

( 5) 0.000298 0.000008 IP 192.168.0.2.57446 > 192.168.0.17.telnet: . ack 1461 wi n 8760

0x0000: 4500 0028 d285 4000 4006 e6e6 c0a8 0002 E..(..@.@...

0x0010: c0a8 0011 e066 0017 3a8a 2bb0 893b dcec ...f..:.+..;..

0x0020: 5010 2238 5f58 0000 P."8_X..

( 6) 0.001097 0.000799 IP 192.168.0.2.57446 > 192.168.0.17.telnet: P 1:9(8) ack 1461 win 8760

0x0000: 4500 0030 d286 4000 4006 e6dd c0a8 0002 E..0..@.@...

0x0010: c0a8 0011 e066 0017 3a8a 2bb0 893b dcec ...f..:.+..;..

0x0020: 5018 2238 7c47 0000 a300 0000 0000 4000 P."8|G...@.

( 7) 0.001151 0.000054 IP 192.168.0.17.telnet > 192.168.0.2.57446: . ack 9 win 6 5519

0x0000: 4500 0028 a6fd 4000 8006 d26e c0a8 0011 E..([email protected]....

0x0010: c0a8 0002 0017 e066 893b dcec 3a8a 2bb8 ...f.;..:.+.

0x0020: 5010 ffef 8198 0000 0204 05b4 0a00 P...

Gathererはまずレングスリプライ取得のため4バイト読むがその値 0x47474747==1195853639(超巨大整数)がリクエストした値より 大きかったのでFATAL ERROR 5で停止した(すなわちGatherer 正常に動作していた)。

何がおこったのか?

(FATAL ERROR 5)

length request

BL0

1でのダンプの解析:

Run_29 (

その

3)

( 8) 0.001367 0.000216 IP 192.168.0.17.telnet > 192.168.0.2.57446: . 1461:2921(1 460) ack 9 win 65519

0x0000: 4500 05dc a6fe 4000 8006 ccb9 c0a8 0011 E...@...

0x0010: c0a8 0002 0017 e066 893b dcec 3a8a 2bb8 ...f.;..:.+.

0x0020: 5010 ffef 57ca 0000 4747 4747 4747 4747 P...W...GGGGGGGG 0x0030: 4747 4747 4747 4747 4747 4747 4747 4747 GGGGGGGGGGGGGGGG 0x0040: 4747 4747 4747 4747 4747 4747 4747 4747 GGGGGGGGGGGGGGGG

0x03e0: 4747 4747 4747 4747 4747 4747 4747 4747 GGGGGGGGGGGGGGGG 0x03f0: 4747 4747 4747 4747 4747 4747 4747 4747 GGGGGGGGGGGGGGGG 0x0400: 4747 4747 0000 01cc 5a13 0dfb 0a22 b43e GGGG....Z....".>

0x0410: 5a13 0f22 0c26 f06b 5a13 0fcc 083a 518f Z..".&.kZ....:Q.

0x0420: 5a13 0ffa 0916 c369 5a13 0fd0 0b0a 8325 Z...iZ...%

0x0430: 5a13 0ff1 0e39 6509 5a13 100c 0f4f c441 Z....9e.Z....O.A 0x0440: 5a13 10fa 0f0f c39c 5a13 1143 0e1e c304 Z...Z..C....

この間全部0x47

途中からそれらしいデータがやってきている。

ドキュメント内 ネットワークプログラミング (ページ 31-81)

関連したドキュメント